home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume23 / cio < prev    next >
Encoding:
Internet Message Format  |  1991-01-08  |  60.7 KB

  1. Subject:  v23i075:  Frontend to RCS ci/co
  2. Newsgroups: comp.sources.unix
  3. Approved: rsalz@uunet.UU.NET
  4. X-Checksum-Snefru: 9eaeecb7 56294d6f 019ba86f f56cff06
  5.  
  6. Submitted-by: Jason Winters <grinch!jason>
  7. Posting-number: Volume 23, Issue 75
  8. Archive-name: cio
  9.  
  10. [  Save this; I hope to be posting RCS shortly...  --r$  ]
  11.  
  12. #! /bin/sh
  13. # This is a shell archive.  Remove anything before this line, then feed it
  14. # into a shell via "sh file" or similar.  To overwrite existing files,
  15. # type "sh file -c".
  16. # The tool that generated this appeared in the comp.sources.unix newsgroup;
  17. # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
  18. # Contents:  Makefile cio.c cio.man
  19. # Wrapped by rsalz@litchi.bbn.com on Wed Dec  5 12:19:58 1990
  20. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  21. echo If this archive is complete, you will see the following message:
  22. echo '          "shar: End of archive."'
  23. if test -f 'Makefile' -a "${1}" != "-c" ; then 
  24.   echo shar: Will not clobber existing file \"'Makefile'\"
  25. else
  26.   echo shar: Extracting \"'Makefile'\" \(315 characters\)
  27.   sed "s/^X//" >'Makefile' <<'END_OF_FILE'
  28. X
  29. X#
  30. X# makefile for cio.c
  31. X#
  32. X
  33. XCFLAGS= -O
  34. XLDFLAGS= 
  35. XLIBS= 
  36. X
  37. XCFILES= cio.c
  38. XOBJS= cio.o
  39. X
  40. Xdefault: cio cii coo
  41. X
  42. Xcii: cio
  43. X    ln cio cii
  44. X
  45. Xcoo: cio
  46. X    ln cio coo
  47. X
  48. Xcio:    $(OBJS)
  49. X    cc $(LDFLAGS) -o cio $(OBJS) $(LIBS)
  50. X
  51. Xinstall: cio
  52. X    cp cio $(HOME)/.bin/coo
  53. X    ln $(HOME)/.bin/coo $(HOME)/.bin/cii
  54. X
  55. Xshar:
  56. X    doshar cio.man makefile cio.c
  57. END_OF_FILE
  58.   if test 315 -ne `wc -c <'Makefile'`; then
  59.     echo shar: \"'Makefile'\" unpacked with wrong size!
  60.   fi
  61.   # end of 'Makefile'
  62. fi
  63. if test -f 'cio.c' -a "${1}" != "-c" ; then 
  64.   echo shar: Will not clobber existing file \"'cio.c'\"
  65. else
  66.   echo shar: Extracting \"'cio.c'\" \(46876 characters\)
  67.   sed "s/^X//" >'cio.c' <<'END_OF_FILE'
  68. X/*----------------------------------------------------------------------------
  69. X/ cio.c - This is a C program to replace the over-grown shell script
  70. X/ that started as something small...
  71. X/
  72. X/ Authors:    Jason P. Winters and Craig J. Kim
  73. X/
  74. X/  We hereby declare this software to be Public Domain.  That means we don't
  75. X/  care what you do with it.  We _would_ prefer that you at least leave our
  76. X/  names in it, as befits Authors, but we can't force you.  Of course, since
  77. X/  it's public domain, all risks/gains using it are your problems.  You don't
  78. X/  have any reason to call us up and complain about it deleting 30megs of
  79. X/  your source code without telling you. ( Software should at least *tell* you
  80. X/  when it does something like that, right? )  :)
  81. X/
  82. X/ Start Date:    November 23, 1988
  83. X/ Revisions:    29-Nov-88 jpw - initial coding completed.
  84. X/        29-Nov-88 cjk - rewrite of front-end logic
  85. X/        30-Nov-88 jpw - Added signals, cleanup of temp files.
  86. X/        30-Nov-88 cjk - added -N option
  87. X/        01-Dec-88 cjk - added usage
  88. X/        01-Dec-88 jpw - added SUID controls for secure files.
  89. X/        02-Dec-88 jpw - added -A option
  90. X/        05-Dec-88 cjk - added '~' commands for message entering
  91. X/        05-Dec-88 cjk - fixed cp to check file status before copying
  92. X/        06-Dec-88 cjk - use separate process for user file input
  93. X/        06-Dec-88 jpw - source now passes System V.2 lint
  94. X/        07-Dec-88 cjk - added environment variable check routine
  95. X/        20-Dec-88 cjk - chmod files in source dir to 0640
  96. X/        21-Dec-88 cjk - put RCS header if not exists
  97. X/        08-Mar-89 cjk - SCCS version
  98. X/        02-Oct-89 jpw - Fixed parsing for header type to insert
  99. X/                        Fixed small bug in directory creation routine
  100. X/        10-May-90 jpw - Changed st += sprintf() code to allow for
  101. X/                        broken sprint() calls.  Failed on Sun
  102. X/                        machines.
  103. X/
  104. X/ Known bugs:
  105. X/       On some systems, the Control-D as end of input in the log entry
  106. X/       routines will cause stdin to be closed, which means the next call
  107. X/       to get an entry (such as a Title file) will fail.  A call to clearerr()
  108. X/       has been added to fix this, but it has not been tested.
  109. X/
  110. X/ To be done:
  111. X/    1. When -U is used, the destination directory should be created if
  112. X/       it does not exist already.  Also, the file mode should be changed to
  113. X/       0640 so that overwriting is not possible by anyone other than
  114. X/       $RCSOWN for security.
  115. X/    2. Full "interactive" mode support
  116. X/   3. Actually use the SCCS version. (Ugh!)
  117. X/---------------------------------------------------------------------------*/
  118. X
  119. X/*#define DEBUG        /* wanna know what's goin' on? */
  120. X/*#define INTERACTIVE    /* enable -I option (incomplete) */
  121. X/*#define void    int    /* if system can't handle void types. */
  122. X#define V_RCS        /* RCS version */
  123. X/*#define V_SCCS        /* SCCS version */
  124. X
  125. X/*--------------------------------------------------- includes -------------*/
  126. X#include <stdio.h>
  127. X#include <string.h>
  128. X#include <ctype.h>
  129. X#include <sys/types.h>
  130. X#include <sys/stat.h>
  131. X#include <errno.h>
  132. X#include <signal.h>
  133. X
  134. X/*--------------------------------------------------- externals ------------*/
  135. Xextern int errno;              /* error code set by system library routines */
  136. X
  137. Xextern FILE *fopen();               /* open a stream (should be in stdio.h) */
  138. Xextern FILE *popen();                 /* open a pipe (should be in stdio.h) */
  139. Xextern char *getenv();                      /* read an environment variable */
  140. Xextern char *tmpnam();                      /* create a temporary file name */
  141. Xextern char *mktemp();                  /* create temp file with a template */
  142. Xextern char *malloc();                        /* allocate a chunk of memory */
  143. Xextern char *getcwd();                             /* get current directory */
  144. Xextern int  unlink();                             /* unlink (delete) a file */
  145. Xextern void exit();                            /* define these for LINT!!!! */
  146. Xextern void perror();                          /* define these for LINT!!!! */
  147. Xextern void free();                            /* define these for LINT!!!! */
  148. X
  149. X/*--------------------------------------------------- defines --------------*/
  150. X#ifndef TRUE
  151. X# define TRUE    1
  152. X# define FALSE    0
  153. X#endif
  154. X
  155. X#define TYPE_LOG    0        /* log message */
  156. X#define TYPE_TITLE    1        /* title message */
  157. X#define MAX_LOG        1020        /* max # of characters for a log */
  158. X
  159. X/*--------------------------------------------------- forwards -------------*/
  160. Xvoid usage(/*_ void _*/);                    /* print program usage message */
  161. Xint  getfinput(/*_ char *name, int type _*/);             /* get title file */
  162. Xint  child_getfinput(/*_ char *name, int type _*/);      /* actual get file */
  163. Xvoid doincmds(/*_ char *argv[], int argc, int in _*/); /* perform user wish */
  164. Xvoid do_ciodir(/*_ char *filenm _*/);    /* do user wish in recursive if -R */
  165. Xint  cio(/*_ char *filenme _*/);                          /* the work horse */
  166. Xint  addrcsext(/*_ char *fname _*/);         /* add RCS file name extension */
  167. Xint  rmrcsext(/*_ char *fname _*/);       /* remove RCS file name extension */
  168. Xvoid inshdr(/*_ char *fname _*/);                      /* insert RCS header */
  169. Xint  makedir(/*_ char *path _*/);    /* make a directory and all its parent */
  170. Xchar *strstr(/*_ char *s1, char *s2 _*/);  /* find a string within a string */
  171. Xchar *strlwr(/*_ char *s _*/);               /* convert upper case to lower */
  172. Xint  asciifile(/*_ char *filename _*/);                  /* check file type */
  173. Xint  rcsfile(/*_ char *filename _*/);             /* check file is RCS file */
  174. Xint  strrd(/*_ char *buf, int max_size,  FILE *fle _*/);/* read from a file */
  175. Xvoid getdir(/*_ void _*/);        /* obtain necessary directory information */
  176. Xint  fix_envstr(/*_ char *cs _*/);  /* remove leading/trailing blanks, etc. */
  177. Xvoid getrcsdir(/*_ char *dir _*/);          /* build rcs dir out of working */
  178. Xvoid getworkdir(/*_ char *dir _*/);         /* build working dir out of rcs */
  179. Xvoid getsrcdir(/*_ char *dir _*/);       /* build source dir out of working */
  180. Xchar *justname(/*_ char *fpath _*/);        /* return just filename portion */
  181. Xchar *memalloc(/*_ int size _*/);  /* allocate memory and check for success */
  182. Xvoid sigcleanup(/*_ void _*/);                 /* cleanup in case interrupt */
  183. Xvoid get_final_id(/*_ void _*/);           /* get user id of RCS file owner */
  184. Xint  nextent(/*_ FILE *fp _*/);    /* read next entry from /etc/passwd file */
  185. X
  186. X/*--------------------------------------------------- globals --------------*/
  187. X#ifdef V_RCS
  188. Xchar *ci_cmd = "ci";                /* command to use to check in a module. */
  189. Xchar *co_cmd = "co";               /* command to use to check out a module. */
  190. X#else
  191. Xchar *ici_cmd = "admin";      /* command to check in a module for 1st time. */
  192. Xchar *ci_cmd = "delta";                    /* command to check in a module. */
  193. Xchar *co_cmd = "get";                     /* command to check out a module. */
  194. X#endif
  195. Xchar *currentdir;                                     /* current directory. */
  196. Xchar *homedir;                                    /* user's home directory. */
  197. Xchar *rcswrk;                                                  /* $RCSWORK. */
  198. Xchar *rcsown;                                                   /* $RCSOWN. */
  199. Xchar *rcsdir;                                                   /* $RCSDIR. */
  200. Xchar *srcdir;                                                   /* $RCSSRC. */
  201. Xchar *headdir;                                                 /* $RCSHEAD. */
  202. Xchar *path;                                                       /* $PATH. */
  203. Xchar *pwdfile = "/etc/passwd";                      /* default passwd file. */
  204. Xchar cioopt[100];                      /* large buffer for command options. */
  205. Xchar d_ent[90];                             /* small buffer for file reads. */
  206. Xchar final[400];                    /* final dir to pass on as destination. */
  207. X#ifdef V_SCCS
  208. Xchar finalfile[400];                  /* final file without SCCS extension. */
  209. X#endif
  210. Xchar logstr[MAX_LOG + 4];                            /* log string to pass. */
  211. Xchar title[100];                                     /* log string to pass. */
  212. Xchar cmdbuf[2400];                                     /* do a single file. */
  213. Xchar editfile[400];                  /* temp edit file, makes cleanup easy. */
  214. Xchar pwdname[20];                                    /* Name found in file. */
  215. Xchar ftypestr[82];                               /* command file(1) output. */
  216. Xchar cii = FALSE;                       /* if set, we are in check in mode. */
  217. Xchar recurse = FALSE;               /* if set, do recursive check in/out's. */
  218. Xchar usertitle = FALSE;                       /* user specified title file. */
  219. Xchar interactive = FALSE;                /* user friendly interactive mode. */
  220. Xchar allfiles = FALSE;                    /* set it TRUE to copy all files. */
  221. Xchar insertheader = FALSE;     /* insert RCS header at the top of the file. */
  222. X#ifdef DEBUG
  223. Xchar noexec = TRUE;                     /* set it FALSE for execution mode. */
  224. Xchar verbose = TRUE;                                    /* be a chatterbox. */
  225. X#else
  226. Xchar noexec = FALSE;                  /* set it TRUE for no execution mode. */
  227. Xchar verbose = FALSE;                                  /* not a chatterbox. */
  228. X#endif
  229. Xchar updsrcdir = FALSE;           /* update master source directory on cii. */
  230. X#ifdef V_SCCS
  231. Xchar do_admin = FALSE;                    /* set it TRUE if first check in. */
  232. X#endif
  233. Xchar *prognam;                                     /* name of this program. */
  234. Xstruct stat filestat;                                  /* file status info. */
  235. Xint s_currentdir;                           /* length of current directory. */
  236. Xint s_homedir;                          /* length of user's home directory. */
  237. Xint s_rcswrk;                                        /* length of $RCSWORK. */
  238. Xint s_rcsdir;                                         /* length of $RCSDIR. */
  239. Xint s_srcdir;                                         /* length of $RCSSRC. */
  240. Xint s_path;                                             /* length of $PATH. */
  241. Xint s_rcsown;                                         /* length of $RCSOWN. */
  242. Xint s_headdir;                                       /* length of $RCSHEAD. */
  243. Xint user_id;                   /* Effective user id to use if ROOT process. */
  244. Xint real_user_id;                      /* Save buffer for original user_id. */
  245. Xint do_unlink;  /* add -l option if not set to avoid unlink of source file. */
  246. X
  247. X/*--------------------------------------------------- main() ----------------
  248. X/ where we begin!
  249. X/---------------------------------------------------------------------------*/
  250. Xmain(argc, argv)
  251. Xint argc;
  252. Xchar *argv[];
  253. X{
  254. X    register int in;
  255. X    register char *cp;        /* used in string updates. */
  256. X
  257. X    prognam = justname(argv[0]);    /* program name */
  258. X    getdir();            /* go get the enviroment pointers. */
  259. X
  260. X    /*
  261. X     * figure what the user wants us to be by reading program name
  262. X     */
  263. X    if (!strcmp("ciitest", prognam) || !strcmp("cootest", prognam))
  264. X    {
  265. X        getrcsdir(final, currentdir);    /* setup variables. */
  266. X        /*
  267. X         * print out our variables here.
  268. X        */
  269. X#ifdef V_RCS
  270. X        (void) printf("Homedir:%s:  rcsdir:%s:  rcswrk:%s:  rcssrc:%s:\n",
  271. X#else
  272. X        (void) printf("HOME:%s:  SCCSDIR:%s:  SCCSWRK:%s:  SCCSSRC:%s:\n",
  273. X#endif
  274. X                homedir ? homedir : "",
  275. X                rcsdir ? rcsdir : "",
  276. X                rcswrk ? rcswrk : "",
  277. X                srcdir ? srcdir : "");
  278. X        (void) printf("Final dir:%s:\n", final);
  279. X        return(0);
  280. X    }
  281. X    if (!strcmp("cii", prognam))    /* this is input.. */
  282. X        cii = TRUE;        /* show we are inputs. */
  283. X    else if (strcmp("coo", prognam))/* it's not this either.. */
  284. X    {
  285. X        (void) printf("Just what did you think this program was, anyway??\n");
  286. X        return(-1);
  287. X    }
  288. X
  289. X    if (!(user_id = geteuid())) /* we are a root process.. */
  290. X    {
  291. X        (void) umask(027); /* set general mask to private modes. */
  292. X        real_user_id = getuid();
  293. X        if (cii)    /* only set this if we are in checkin mode. */
  294. X            get_final_id(); /* go get the final user ID for file creation. */
  295. X        else
  296. X            user_id = real_user_id;    /* otherwise, use owners real one. */
  297. X        (void) setuid(user_id); /* and, fix up the user id now. */
  298. X#ifdef DEBUG
  299. X        (void) printf("Using Uid:%d:\n", user_id);
  300. X#endif
  301. X    }
  302. X    else
  303. X        real_user_id = user_id;    /* else they should match. */
  304. X
  305. X    /* prepare for disasters */
  306. X    (void) signal(SIGINT, (int (*)()) sigcleanup);
  307. X    (void) signal(SIGQUIT, (int (*)()) sigcleanup);
  308. X
  309. X    cp = cioopt;
  310. X    title[0] = '\0';  /* make sure no titles are required. */
  311. X    for (in = 1; argv[in][0] == '-'; in++) /* while chars here.. */
  312. X    {
  313. X        switch ((int) argv[in][1])    /* what option? */
  314. X        {
  315. X        case '?':        /* print program usage */
  316. X        case '-':
  317. X            usage();
  318. X            return(0);
  319. X        case 'A':        /* copy all files */
  320. X            allfiles = !allfiles;
  321. X            break;
  322. X#ifdef INTERACTIVE
  323. X        case 'I':        /* interactive mode */
  324. X            interactive = !interactive;
  325. X            break;
  326. X#endif
  327. X        case 'H':        /* RCS/SCCS header */
  328. X            insertheader = !insertheader;
  329. X            break;
  330. X        case 'N':        /* do not execute */
  331. X            noexec = !noexec;
  332. X            break;
  333. X        case 'R':        /* Recursion flag. */
  334. X            recurse = !recurse;
  335. X            break;
  336. X        case 'T':        /* get Title flag. */
  337. X            if(cii)    /* get a title file and name. */
  338. X                (void) getfinput(title, TYPE_TITLE);
  339. X            break;
  340. X        case 'U':        /* update working directory */
  341. X            updsrcdir = !updsrcdir;
  342. X            break;
  343. X        case 'V':        /* verbose */
  344. X            verbose = !verbose;
  345. X            break;
  346. X#ifdef V_RCS
  347. X        case 'm':        /* they gave us one */
  348. X#else
  349. X        case 'y':        /* they gave us one */
  350. X#endif
  351. X            (void) strcpy(logstr, &argv[in][1]); /* copy across. */
  352. X            break;
  353. X        case 't':        /* user gave us one */
  354. X            (void) strcpy(title, &argv[in][1]);
  355. X            usertitle = TRUE;
  356. X            break;
  357. X        default:        /* must be a ci or co command */
  358. X            (void) sprintf(cp, " %s", argv[in]); /* append */
  359. X            cp += strlen(cp); /* skip to end of string. */
  360. X            break;
  361. X        }
  362. X    }
  363. X#ifdef INTERACTIVE
  364. X    if (noexec && interactive)    /* resolve conflict of interest */
  365. X        interactive = FALSE;
  366. X#endif
  367. X    doincmds(argv, argc, in);    /* do the check in command. */
  368. X    if (title[0] && !usertitle)    /* if file is here. */
  369. X        (void) unlink(title);    /* zap it! */
  370. X    return(0);            /* we did it good! */
  371. X}
  372. X
  373. X/*--------------------------------------------------- usage() ---------------
  374. X/ print program usage information
  375. X/---------------------------------------------------------------------------*/
  376. Xvoid
  377. Xusage()
  378. X{
  379. X    char *inout = cii ? "in" : "out";
  380. X
  381. X#ifdef INTERACTIVE
  382. X    (void) fprintf(stderr, "Usage: %s [-A] %s[-I] [-N] [-R] %s",
  383. X#else
  384. X    (void) fprintf(stderr, "Usage: %s [-A] %s[-N] [-R] %s",
  385. X#endif
  386. X            prognam, cii ? "[-H] " : "",
  387. X                 cii ? "[-T] [-U] " : "");
  388. X    (void) fprintf(stderr, "[-V] [%s options] [[filename]...]\n",
  389. X            cii ? ci_cmd : co_cmd);
  390. X    (void) fprintf(stderr, "     -A : check %s all files\n", inout);
  391. X#ifdef INTERACTIVE
  392. X    (void) fprintf(stderr, "     -I : interactive mode\n");
  393. X#endif
  394. X    if (cii)
  395. X#ifdef V_RCS
  396. X        (void) fprintf(stderr, "     -H : attach RCS header\n");
  397. X#else
  398. X        (void) fprintf(stderr, "     -H : attach SCCS header\n");
  399. X#endif
  400. X    (void) fprintf(stderr, "     -N : no execute mode\n");
  401. X    (void) fprintf(stderr, "     -R : recursive check %s\n", inout);
  402. X    if (cii)
  403. X    {
  404. X        (void) fprintf(stderr, "     -T : create title file\n");
  405. X        (void) fprintf(stderr, "     -U : update source directory\n");
  406. X    }
  407. X    (void) fprintf(stderr, "     -V : verbose mode\n");
  408. X}
  409. X
  410. X/*--------------------------------------------------- sigcleanup() ----------
  411. X/ cleanup before exiting. 
  412. X/---------------------------------------------------------------------------*/
  413. Xvoid
  414. Xsigcleanup()
  415. X{
  416. X    (void) printf("\n[%s: Interrupted]\n", prognam);
  417. X    if (title[0] && !usertitle)    /* remove title file, if here. */
  418. X        (void) unlink(title);
  419. X    if (editfile[0])        /* if a temp file might be here. */
  420. X        (void) unlink(editfile);    /* attempt to remove it.*/
  421. X    exit(0);
  422. X}
  423. X
  424. X/*--------------------------------------------------- doincmds() ------------
  425. X/ actual do routine
  426. X/---------------------------------------------------------------------------*/
  427. Xvoid
  428. Xdoincmds(argv, argc, in)
  429. Xchar *argv[];
  430. Xint argc, in;
  431. X{
  432. X    register char *cp;
  433. X    char entry[400];
  434. X
  435. X    if (cii)
  436. X    {
  437. X        if (argc == in)        /* no arguments given */
  438. X        {
  439. X            do_ciodir(currentdir);
  440. X            return /* void */;
  441. X        }
  442. X        for ( ; in < argc; in++)
  443. X        {
  444. X            (void) sprintf(entry, "%s/%s", currentdir, argv[in]);
  445. X#ifdef DEBUG
  446. X            (void) printf("Processing %s\n", entry);
  447. X#endif
  448. X            if (stat(entry, &filestat))
  449. X            {
  450. X#ifdef DEBUG
  451. X                (void) printf("Unable to stat(2) %s\n", entry);
  452. X#endif
  453. X                continue;
  454. X            }
  455. X            do_unlink = (filestat.st_uid == real_user_id);
  456. X            if ((filestat.st_mode & S_IFMT) == S_IFDIR)
  457. X                do_ciodir(entry);
  458. X            else
  459. X                (void) cio(entry);
  460. X        }
  461. X    }
  462. X    else /* coo */
  463. X    {
  464. X        char *ep;
  465. X
  466. X        getrcsdir(entry, currentdir);
  467. X        if (argc == in)
  468. X        {
  469. X            do_ciodir(entry);
  470. X            return /* void */;
  471. X        }
  472. X        for (ep = entry; *ep; ep++)
  473. X            ;
  474. X        for ( ; in < argc; in++)
  475. X        {
  476. X            (void) sprintf(ep, "/%s", argv[in]);
  477. X#ifdef DEBUG
  478. X            (void) printf("Processing %s\n", entry);
  479. X#endif
  480. X            if (stat(entry, &filestat))
  481. X            {
  482. X                if (!addrcsext(entry))
  483. X                    continue;
  484. X                if (stat(entry, &filestat))
  485. X                    continue;
  486. X            }
  487. X            do_unlink = (filestat.st_uid == real_user_id);
  488. X            if ((filestat.st_mode & S_IFMT) == S_IFDIR)
  489. X                do_ciodir(entry);
  490. X            else
  491. X                (void) cio(entry);
  492. X        }
  493. X            
  494. X    }
  495. X    return /* void */;
  496. X}
  497. X
  498. X/*--------------------------------------------------- do_ciodir() -----------
  499. X/ actual do routine - Warning: RECURSIVE
  500. X/---------------------------------------------------------------------------*/
  501. Xvoid
  502. Xdo_ciodir(dir)
  503. Xchar *dir;
  504. X{
  505. X    FILE *pp;
  506. X    char *cmd, *entry;
  507. X
  508. X    (void) sprintf(cmd = memalloc(strlen(dir) + 4), "ls %s", dir);
  509. X    pp = popen(cmd, "r");
  510. X    free(cmd);
  511. X    if (!pp)
  512. X        return /* void */;
  513. X    entry = memalloc(strlen(dir) + 30);
  514. X    while (strrd(d_ent, 80, pp) != -1)
  515. X    {
  516. X        (void) sprintf(entry, "%s/%s", dir, d_ent);
  517. X        if (stat(entry, &filestat))
  518. X        {
  519. X#ifdef DEBUG
  520. X            (void) printf("Unable to stat(2) %s\n", entry);
  521. X#endif
  522. X            continue;
  523. X        }
  524. X        do_unlink = (filestat.st_uid == real_user_id);
  525. X        if ((filestat.st_mode & S_IFMT) == S_IFDIR)
  526. X        {
  527. X            if (recurse)
  528. X                do_ciodir(entry);
  529. X            else
  530. X                continue;
  531. X        }
  532. X        else
  533. X            (void) cio(entry);
  534. X    }
  535. X    free(entry);
  536. X    (void) pclose(pp);
  537. X    return /* void */;
  538. X}
  539. X
  540. X/*--------------------------------------------------- cio() -----------------
  541. X/ do this to file, if we can.
  542. X/---------------------------------------------------------------------------*/
  543. Xint
  544. Xcio(filename)
  545. Xchar *filename;
  546. X{
  547. X    register char *cp, *st;        /* some char pointers */
  548. X    int do_copy = FALSE;        /* do cp(1) */
  549. X    char titlest[100];        /* for file names. */
  550. X
  551. X    if (cii)
  552. X    {
  553. X        if (!asciifile(filename))    /* if not ascii file */
  554. X        {
  555. X            if (!allfiles)
  556. X                return(FALSE);    /* don't if not forced copy */
  557. X            do_copy = TRUE;
  558. X        }
  559. X        else if (insertheader)        /* inserting default header */
  560. X        {
  561. X            inshdr(filename);
  562. X        }
  563. X    }
  564. X    else if (!rcsfile(filename))        /* not an RCS file */
  565. X    {
  566. X        if (!allfiles)
  567. X            return(FALSE);
  568. X        do_copy = TRUE;            /* simply cp(1) it */
  569. X    }
  570. X
  571. X    if (cii && !do_copy && !logstr[0]) /* we don't have a log entry yet. */
  572. X    {
  573. X        if (noexec)
  574. X        {
  575. X            (void) printf("Logfile entry bypassed.\n");
  576. X            (void) strcpy(logstr, " ");    /* fake it */
  577. X        }
  578. X        else if (!getfinput(titlest, TYPE_LOG)) /* if we entered anything */
  579. X        {
  580. X            FILE *fp;
  581. X            char buf[100];
  582. X            register char *bp;
  583. X            register int numchars = 0;
  584. X
  585. X            if (fp = fopen(titlest, "r"))
  586. X            {
  587. X                st = logstr;
  588. X#ifdef V_RCS
  589. X                (void) sprintf(st, " -m\"");
  590. X#else
  591. X                (void) sprintf(st, " -y\"");
  592. X#endif
  593. X                st += strlen(st); /* skip to end of string. */
  594. X                while (strrd(buf, 80, fp) != -1)
  595. X                {    /*
  596. X                     * escape quotes (") characters
  597. X                     */
  598. X                    bp = buf;
  599. X                    while (*bp)
  600. X                    {
  601. X                        if (*bp == '"')
  602. X                            if (numchars < MAX_LOG)
  603. X                            {
  604. X                                numchars++;
  605. X                                *st++ = '\\'; /*escape*/
  606. X                            }
  607. X                        if (numchars < MAX_LOG)
  608. X                        {
  609. X                            numchars++;
  610. X                            *st++ = *bp++; /* append */
  611. X                        }
  612. X                    }
  613. X                    *st++ = '\n'; /* add end of line now. */
  614. X                }
  615. X                if (*(st - 1) == '\n')
  616. X                    *(st - 1) = '"';
  617. X                else
  618. X                    *st++ = '"';
  619. X                *st = '\0';
  620. X                (void) fclose(fp);
  621. X                if (numchars >= MAX_LOG)
  622. X                    (void) printf("Log entry truncated.\n");
  623. X            }
  624. X            (void) unlink(titlest); /* cleanup. */
  625. X        }
  626. X    }
  627. X
  628. X    titlest[0] = '\0'; /* cleanup string reference. */
  629. X    if (cii)
  630. X        getrcsdir(final, filename);
  631. X    else
  632. X        getworkdir(final, filename);
  633. X
  634. X    if (st = strrchr(final, '/'))    /* if this has a sub dir. */
  635. X    {
  636. X        *st = '\0';    /* terminate it at the directoy level. */
  637. X        if (access(final, 0))    /* it's not here... */
  638. X            if (!makedir(final))    /* so try and make it. */
  639. X            {
  640. X                (void) printf("Could not create directory :%s:\n", final);
  641. X                return(FALSE);
  642. X            }
  643. X        *st = '/';    /* restore the rest of the file name. */
  644. X    }
  645. X
  646. X    if (cii)
  647. X    {
  648. X        if (!do_copy)    /* not in binary mode. */
  649. X        {    /*
  650. X             * make the new file name and check if it's here
  651. X             */
  652. X#ifdef V_SCCS
  653. X            (void) strcpy(finalfile, final);
  654. X#endif
  655. X            (void) addrcsext(final);
  656. X            if (!noexec && access(final, 0))
  657. X            {
  658. X                /*
  659. X                 * if not, need to get a title message for it
  660. X                 */
  661. X                if (!title[0]) /* no title file yet. */
  662. X                    (void) getfinput(title, TYPE_TITLE); /* get one */
  663. X                if (title[0])  /* if we've one, build command */
  664. X                    (void) sprintf(titlest, " -t%s ", title);
  665. X#ifdef V_SCCS
  666. X                do_admin = TRUE;    /* 1st check-in */
  667. X#endif
  668. X            }
  669. X#ifdef V_SCCS
  670. X            else
  671. X                do_admin = FALSE;
  672. X#endif
  673. X        }
  674. X        else if (noexec)
  675. X            (void) strcpy(titlest, " -tfile_name ");
  676. X    }
  677. X    else    /* check out mode. */
  678. X    {    /*
  679. X         * find a comma to make the new name.
  680. X         * if found one, strip it to make a new name.
  681. X         */
  682. X        (void) rmrcsext(final);
  683. X    }
  684. X
  685. X    /*
  686. X     * Build command string
  687. X     */
  688. X    if (do_copy)    /* we want to just copy the file now. */
  689. X    {
  690. X        if (!interactive && !noexec && !access(final, 0))
  691. X        {
  692. X            (void) printf("%s already exists.  Overwrite? (yes) ", final);
  693. X            (void) strrd(cmdbuf, 20, stdin);
  694. X            (void) strlwr(cmdbuf);
  695. X            if (strncmp(cmdbuf, "yes", strlen(cmdbuf)))
  696. X                return(TRUE);
  697. X        }
  698. X        (void) sprintf(cmdbuf, "cp %s %s", filename, final); /* copy command */
  699. X    }
  700. X    else if (cii)
  701. X    {
  702. X#ifdef V_RCS
  703. X        (void) sprintf(cmdbuf, "%s%s%s%s%s %s %s",
  704. X            ci_cmd, cioopt, titlest, logstr,
  705. X            do_unlink ? "" : " -l", filename, final);
  706. X#else
  707. X        st = cmdbuf;
  708. X        (void) sprintf(st, "cp %s %s; ", filename, finalfile);
  709. X        st += strlen(st); /* skip to end of string. */
  710. X        if (cp = strrchr(final, '/'))
  711. X        {
  712. X            *cp = '\0';
  713. X            (void) sprintf(st, "cd %s; ", final);
  714. X            st += strlen(st); /* skip to end of string. */
  715. X            *cp = '/';
  716. X        }
  717. X        if (do_admin)
  718. X        {
  719. X            (void) sprintf(st, "%s -i%s%s%s%s %s",
  720. X                ici_cmd, justname(finalfile),
  721. X                cioopt, titlest, logstr, justname(final));
  722. X            st += strlen(st); /* skip to end of string. */
  723. X        }
  724. X        else
  725. X        {
  726. X            (void) sprintf(st, "%s%s%s %s",
  727. X                ci_cmd, cioopt, logstr, justname(final));
  728. X            st += strlen(st); /* skip to end of string. */
  729. X        }
  730. X        if (do_unlink)
  731. X        {
  732. X            (void) sprintf(st, "; rm %s", filename);
  733. X            st += strlen(st); /* skip to end of string. */
  734. X        }
  735. X#endif
  736. X    }
  737. X    else    /* coo */
  738. X    {
  739. X#ifdef V_RCS
  740. X        (void) sprintf(cmdbuf, "%s%s %s %s",
  741. X                co_cmd, cioopt, filename, final);
  742. X#else
  743. X        st = cmdbuf;
  744. X        (void) sprintf(st, "rm -f %s; ", final);
  745. X        st += strlen(st); /* skip to end of string. */
  746. X        if (cp = strrchr(filename, '/'))
  747. X        {
  748. X            *cp = '\0';
  749. X            (void) sprintf(st, "cd %s; ", filename);
  750. X            st += strlen(st); /* skip to end of string. */
  751. X            *cp = '/';
  752. X        }
  753. X        addrcsext(filename);
  754. X        (void) sprintf(st, "%s%s %s", co_cmd, cioopt,
  755. X            justname(filename));
  756. X        st += strlen(st); /* skip to end of string. */
  757. X        if (cp = strrchr(final, '/'))
  758. X        {
  759. X            cp++;
  760. X            (void) sprintf(st, "; mv %s %s", cp, final);
  761. X            st += strlen(st); /* skip to end of string. */
  762. X        }
  763. X#endif
  764. X    }
  765. X
  766. X#ifdef INTERACTIVE
  767. X    if (interactive)
  768. X    {
  769. X        char ans[20];
  770. X        int done = FALSE;
  771. X
  772. X        do
  773. X        {
  774. X            (void) printf("%s\nExecute? (yes) ", cmdbuf);
  775. X            (void) strrd(ans, 20, stdin);
  776. X            st = ans;
  777. X            while (isspace(*st))
  778. X                st++;
  779. X            if (!*st)
  780. X                (void) strcpy(st, "yes");
  781. X            (void) strlwr(st);
  782. X            if (!strncmp(st, "yes", strlen(st))) {
  783. X                done = TRUE;
  784. X                (void) system(cmdbuf);    /* do it. */
  785. X            }
  786. X            else if (*st == '?')
  787. X            {
  788. X                (void) printf("\n");
  789. X                (void) printf("yes     Do it\n");
  790. X                (void) printf("no      Don't do it\n");
  791. X                (void) printf("view    View current file\n");
  792. X                (void) printf("?       Print this message\n");
  793. X                (void) printf("\n");
  794. X            }
  795. X            else if (!strncmp(st, "view", strlen(st)))
  796. X            {
  797. X                (void) printf("Not implemented yet!\n\n");
  798. X            }
  799. X            else /* take it as "no" */
  800. X                done = TRUE;
  801. X        } while (!done);
  802. X    }
  803. X    else
  804. X    {
  805. X#endif /* INTERACTIVE */
  806. X        if (verbose)
  807. X            (void) printf("%s command :%s:\n", prognam, cmdbuf);
  808. X        if (!noexec)
  809. X        {
  810. X            if (do_copy && !verbose)
  811. X                (void) printf("%s\n", cmdbuf);
  812. X            (void) system(cmdbuf);    /* do it. */
  813. X        }
  814. X        else if (!verbose)    /* if don't want to exec, don't */
  815. X            (void) printf("%s\n", cmdbuf);
  816. X#ifdef INTERACTIVE
  817. X    }
  818. X#endif /* INTERACTIVE */
  819. X    if (updsrcdir)            /* update source directory */
  820. X    {
  821. X        getsrcdir(final, filename);
  822. X        (void) sprintf(cmdbuf, "cp %s %s", filename, final);
  823. X        if (noexec)        /* don't actual do it */
  824. X            (void) printf("%s\n", cmdbuf);
  825. X        else
  826. X        {
  827. X            if (verbose)    /* speak, yo wise one! */
  828. X                (void) printf("%s command :%s:\n", prognam, cmdbuf);
  829. X            if (!strcmp(filename, final))
  830. X                (void) printf("Source and destination identical.  Not updated.\n");
  831. X            else
  832. X            {
  833. X                (void) chmod(final, 0640);
  834. X                (void) system(cmdbuf);    /* do it! */
  835. X            }
  836. X        }
  837. X    }
  838. X    return(TRUE);
  839. X}
  840. X
  841. X/*--------------------------------------------------- addrcsext() -----------
  842. X/ add RCS file extension ",v" if there isn't one already
  843. X/---------------------------------------------------------------------------*/
  844. Xint
  845. Xaddrcsext(fname)
  846. Xchar *fname;
  847. X{
  848. X    register char *cp;
  849. X
  850. X#ifdef V_RCS
  851. X    for (cp = fname; *cp; cp++)
  852. X        ;
  853. X    if (*--cp == 'v' && *(cp - 1) == ',')
  854. X        return(0);        /* already there */
  855. X
  856. X    *++cp = ',';            /* add ",v" */
  857. X    *++cp = 'v';
  858. X    *++cp = '\0';
  859. X#else
  860. X    char t_name[20];
  861. X
  862. X    cp = justname(fname);
  863. X    if (*cp == 's' && *(cp + 1) == '.')
  864. X        return(0);
  865. X    (void) strcpy(t_name, cp);
  866. X    *cp++ = 's';            /* add "s." in front of file name */
  867. X    *cp++ = '.';
  868. X    (void) strcpy(cp, t_name);
  869. X#endif
  870. X    return(1);
  871. X}
  872. X
  873. X/*--------------------------------------------------- rmrcsext() ------------
  874. X/ remove RCS extension if there is one; returns 1 if remove, else 0
  875. X/---------------------------------------------------------------------------*/
  876. Xint
  877. Xrmrcsext(fname)
  878. Xchar *fname;
  879. X{
  880. X    register char *cp;
  881. X
  882. X#ifdef V_RCS
  883. X    for (cp = fname; *cp; cp++)
  884. X        ;
  885. X    if (*--cp == 'v' && *--cp == ',')
  886. X    {
  887. X        *cp = '\0';
  888. X        return(1);
  889. X    }
  890. X#else
  891. X    cp = justname(fname);
  892. X    if (*cp == 's' && *(cp + 1) == '.')
  893. X    {
  894. X        (void) strcpy(cp, cp + 2);
  895. X        return(1);
  896. X    }
  897. X#endif
  898. X    return(0);
  899. X}
  900. X
  901. X/*--------------------------------------------------- inshdr() --------------
  902. X/ insert RCS header if none exists already
  903. X/---------------------------------------------------------------------------*/
  904. Xvoid
  905. Xinshdr(t_name)
  906. Xchar *t_name;
  907. X{
  908. X#    define FTYPE_C        0    /* C program text */
  909. X#    define FTYPE_S        1    /* assembly program text */
  910. X#    define FTYPE_SH        2    /* shell script */
  911. X#    define FTYPE_ROFF    3    /* nroff, tbl, eqn, etc */
  912. X#    define FTYPE_F        4    /* Fortran program text */
  913. X#    define FTYPE_DEFAULT    5    /* don't know */
  914. X#    define FTYPE_MK        6    /* makefile script */
  915. X#    define FTYPE_H        7    /* C header file text */
  916. X
  917. X    static struct _ftype {
  918. X        char *keyword;        /* phrase may exist in file(1) output */
  919. X        char *header;        /* header template file name. */
  920. X    } ftype[] = {
  921. X#ifdef V_RCS
  922. X        { "c program",    ".rcshead.c"    },    /* FTYPE_C */
  923. X        { "assembler",    ".rcshead.s"    },    /* FTYPE_S */
  924. X        { "command",    ".rcshead.sh"    },    /* FTYPE_SH */
  925. X        { "roff, tbl",    ".rcshead.roff"    },    /* FTYPE_ROFF */
  926. X        { "fortran",    ".rcshead.f"    },    /* FTYPE_F */
  927. X        { 0,        ".rcshead"    },    /* FTYPE_DEFAULT */
  928. X        { 0,        ".rcshead.mk"    },    /* FTYPE_MK */
  929. X        { 0,        ".rcshead.h"    } };    /* FTYPE_H */
  930. X#else
  931. X        { "c program",    ".sccshead.c"    },    /* FTYPE_C */
  932. X        { "assembler",    ".sccshead.s"    },    /* FTYPE_S */
  933. X        { "command",    ".sccshead.sh"    },    /* FTYPE_SH */
  934. X        { "roff, tbl",    ".sccshead.roff"},    /* FTYPE_ROFF */
  935. X        { "fortran",    ".sccshead.f"    },    /* FTYPE_F */
  936. X        { 0,        ".sccshead"    },    /* FTYPE_DEFAULT */
  937. X        { 0,        ".sccshead.mk"    },    /* FTYPE_MK */
  938. X        { 0,        ".sccshead.h"    } };    /* FTYPE_H */
  939. X#endif /* V_RCS */
  940. X    static struct _fext {
  941. X        char *name;
  942. X        int type;
  943. X    } fext[] = {
  944. X        { ".c",        FTYPE_C        },
  945. X        { ".h",        FTYPE_H        },
  946. X        { ".s",        FTYPE_S        },
  947. X        { ".f",        FTYPE_F        },
  948. X        { ".man",    FTYPE_ROFF    },
  949. X        { ".mk",    FTYPE_MK    },
  950. X        { ".1",        FTYPE_ROFF    },
  951. X        { ".2",        FTYPE_ROFF    },
  952. X        { ".3",        FTYPE_ROFF    },
  953. X        { ".4",        FTYPE_ROFF    },
  954. X        { ".5",        FTYPE_ROFF    },
  955. X        { ".6",        FTYPE_ROFF    },
  956. X        { ".7",        FTYPE_ROFF    },
  957. X        { ".8",        FTYPE_ROFF    },
  958. X        { ".9",        FTYPE_ROFF    },
  959. X        { 0,        0        } };
  960. X    FILE *ifp, *ofp;
  961. X    char buf[4096], headfile[128], tempfile[20], fname[40];
  962. X    register int i, c, ft = FTYPE_DEFAULT, err=0;
  963. X    register char *ext;
  964. X
  965. X    strcpy(fname, justname(t_name)); /* copy over only name. */
  966. X
  967. X    if (!(ifp = fopen(t_name, "r")))    /* quickly check for RCS header */
  968. X        return;
  969. X                    /* we are looking for "$Header" */
  970. X                    /* within first 50 lines of the file */
  971. X    for (i = 0; strrd(buf, 128, ifp) > 0 && i < 50; i++)
  972. X#ifdef V_RCS
  973. X        if (strstr(buf, "$Header"))
  974. X#else
  975. X        if (strstr(buf, "#ident"))
  976. X#endif
  977. X        {
  978. X            (void) fclose(ifp);
  979. X            if (verbose)
  980. X#ifdef V_RCS
  981. X                (void) printf("%s already has a RCS header.\n",
  982. X#else
  983. X                (void) printf("%s already has a SCCS header.\n",
  984. X#endif /* V_RCS */
  985. X                        t_name);
  986. X            return;
  987. X        }
  988. X
  989. X    (void) fclose(ifp);
  990. X                    /* examine file(1) output */
  991. X    for (i = 0; ftype[i].keyword; i++)
  992. X        if (strstr(ftypestr, ftype[i].keyword))
  993. X        {
  994. X            ft = i;
  995. X            break;        /* found one */
  996. X        }
  997. X
  998. X    if (!ftype[i].keyword)        /* file(1) didn't help */
  999. X    {                /* examine file extension */
  1000. X        if (ext = strrchr(fname, '.'))
  1001. X        {
  1002. X            for (i = 0; fext[i].name; i++)
  1003. X                if (!strcmp(ext, fext[i].name))
  1004. X                    ft = fext[i].type;
  1005. X        }
  1006. X        else
  1007. X        {            /* check if makefile script */
  1008. X            (void) strcpy(buf, fname);
  1009. X            (void) strlwr(buf);
  1010. X            if (!strcmp(buf, "makefile") || !strcmp(buf, "Makefile"))
  1011. X                ft = FTYPE_MK; /* If either of two fixed names.. */
  1012. X        }
  1013. X    }
  1014. X    else if (ft == FTYPE_C)        /* see if source or header */
  1015. X    {
  1016. X        if ((ext = strrchr(fname, '.')) && !strcmp(ext, ".h"))
  1017. X            ft = FTYPE_H;
  1018. X    }
  1019. X    if (verbose)            /* is this necessary */
  1020. X        (void) printf("%s is type [%d]\n", fname, ft);
  1021. X    if (noexec)            /* no execution mode */
  1022. X        return;
  1023. X
  1024. X    (void) sprintf(headfile, "%s%s", headdir, ftype[ft].header);
  1025. X    if (!(ifp = fopen(headfile, "r")))
  1026. X    {
  1027. X        (void) printf("Unable to open header template file [%s]\n",
  1028. X                headfile);
  1029. X        return;
  1030. X    }
  1031. X    /* build a tmp file in the same directory as old file. */
  1032. X    strcpy(tempfile, t_name);
  1033. X    ext = justname(tempfile); /* find end of path. */
  1034. X    *ext = '\0'; /* and terminate path there. */
  1035. X    (void) strcat(tempfile, "ciotmp._XXXXXX"); /* add tmp name */
  1036. X    (void) mktemp(tempfile);        /* generate temp file name */
  1037. X    if (!(ofp = fopen(tempfile, "w")))    /* open temp file */
  1038. X    {
  1039. X        (void) printf("Unable to open temporary file [%s]\n", tempfile);
  1040. X        (void) fclose(ifp);
  1041. X        return;
  1042. X    }
  1043. X    while ((c = fgetc(ifp)) != EOF)        /* copy header first */
  1044. X        (void) fputc(c, ofp);
  1045. X    (void) fclose(ifp);
  1046. X    if (!(ifp = fopen(t_name, "r")))        /* open check-in file */
  1047. X    {
  1048. X        (void) printf("Unable to open [%s] for read\n", t_name);
  1049. X        (void) fclose(ofp);
  1050. X        (void) unlink(tempfile);
  1051. X        return;
  1052. X    }
  1053. X    while ((c = fgetc(ifp)) != EOF)        /* append to temp file */
  1054. X        if(fputc(c, ofp) == EOF)
  1055. X            err=1; /* couldn't write error. */
  1056. X    (void) fclose(ifp);            /* done */
  1057. X    (void) fclose(ofp);
  1058. X
  1059. X/* ok.  It's hard to make sure that everthing has gone well; if we unlink
  1060. X   the src file and can't link the temp file, we could lose everthing.
  1061. X   So, if copy fails, leave temp file alone, as it may be the only copy
  1062. X   we have left!  If the unlinking the original fails, we can remove the
  1063. X   copy, as we don't need it.
  1064. X*/
  1065. X    if(!err && !unlink(t_name))    /* 'mv tempfile fname' */
  1066. X    {
  1067. X        if(!link(tempfile, t_name)) /* 'cp tempfile t_name' */
  1068. X            (void) unlink(tempfile);        /* 'rm tempfile' */
  1069. X        else
  1070. X            (void) printf("Link of %s and %s failed after removing %s.\n%s not removed.\n",
  1071. X                tempfile, t_name, t_name, tempfile);
  1072. X    }
  1073. X    else
  1074. X    {
  1075. X        (void) unlink(tempfile);        /* 'rm tempfile' */
  1076. X        (void) printf("Could not insert header into %s.  Copy failed.\n", t_name);
  1077. X    }
  1078. X}
  1079. X
  1080. X/*--------------------------------------------------- makedir() -------------
  1081. X/ make a directory path, with recursion.
  1082. X/ returns TRUE if successful, FALSE otherwise.
  1083. X/
  1084. X/ This really needs to be re-written.  It works, but that's all I can really
  1085. X/ say for it...  Hey, *I* don't have mkdir() calls!
  1086. X/---------------------------------------------------------------------------*/
  1087. Xint
  1088. Xmakedir(newpath)
  1089. Xchar *newpath;        /* path name to make. */
  1090. X{
  1091. X    register char *st, *cp;
  1092. X
  1093. X    if(!*newpath) return(FALSE); /* skip last directory attempt. */
  1094. X    cp = memalloc(strlen(newpath) + 24);
  1095. X    (void) sprintf(cp, "/bin/mkdir %s 2>/dev/null", newpath);
  1096. X    if(verbose)
  1097. X        (void) printf("calling mkdir: %s\n", cp);
  1098. X    if (noexec)
  1099. X    {
  1100. X        (void) printf("%s\n", cp);
  1101. X        free(cp);
  1102. X        return(TRUE);
  1103. X    }
  1104. X    if (system(cp))            /* it failed.. */
  1105. X    {
  1106. X        (void) strcpy(cp, newpath);    /* get current one. */
  1107. X        st = strrchr(cp, '/'); /* remove one more layer.. */
  1108. X        *st = '\0';        /* terminate here. */
  1109. X        if (makedir(cp) == FALSE)    /* try and build next level back. */
  1110. X        {
  1111. X            free(cp);
  1112. X            return(FALSE);
  1113. X        } /* ok, so.. it passed on back. Try this again. */
  1114. X        else if(makedir(newpath) == FALSE)
  1115. X        {
  1116. X            free(cp);
  1117. X            return(FALSE);
  1118. X        }
  1119. X    }
  1120. X    free(cp);
  1121. X    return(TRUE);
  1122. X}
  1123. X
  1124. X/*--------------------------------------------------- strstr() --------------
  1125. X/ find a substring within a string
  1126. X/---------------------------------------------------------------------------*/
  1127. Xchar *
  1128. Xstrstr(s1, s2)
  1129. Xregister char *s1, *s2;
  1130. X{
  1131. X    register int l;
  1132. X
  1133. X    if (l = strlen(s2))
  1134. X        for ( ; s1 = strchr(s1, s2[0]); s1++)
  1135. X            if (memcmp(s1, s2, l) == 0)
  1136. X                break;
  1137. X    return(s1);
  1138. X}
  1139. X
  1140. X/*--------------------------------------------------- strlwr() ---------------
  1141. X/ strlwr.c - convert passed string to its equivalent lowercases
  1142. X/----------------------------------------------------------------------------*/
  1143. Xchar *
  1144. Xstrlwr(s)
  1145. Xregister char *s;
  1146. X{
  1147. X    char *op;
  1148. X
  1149. X    for (op = s; *s; s++)
  1150. X        if (isupper(*s))
  1151. X            *s = _tolower(*s);
  1152. X    return(op);
  1153. X}
  1154. X
  1155. X/*--------------------------------------------------- asciifile() -----------
  1156. X/ check if passed file is an ascii file using file(1) command
  1157. X/---------------------------------------------------------------------------*/
  1158. Xint
  1159. Xasciifile(fn)
  1160. Xchar *fn;
  1161. X{
  1162. X    char cmdstr[256];
  1163. X    register FILE *fp;
  1164. X
  1165. X    (void) sprintf(cmdstr, "file %s", fn);
  1166. X    if (!(fp = popen(cmdstr, "r")))
  1167. X        return(FALSE);
  1168. X    (void) strrd(ftypestr, 80, fp); /* get a line. */
  1169. X    (void) pclose(fp);    /* and done. */
  1170. X#ifdef DEBUG
  1171. X    (void) printf("%s\n", cmdstr);
  1172. X#endif
  1173. X    if (strstr(ftypestr, "text"))
  1174. X        return(TRUE);
  1175. X    return(FALSE);
  1176. X}
  1177. X
  1178. X/*--------------------------------------------------- rcsfile() -------------
  1179. X/ check if passed file is an RCS file using file(1) command
  1180. X/---------------------------------------------------------------------------*/
  1181. Xint
  1182. Xrcsfile(fn)
  1183. Xchar *fn;
  1184. X{
  1185. X    char cmdstr[256];
  1186. X    register FILE *fp;
  1187. X
  1188. X    (void) sprintf(cmdstr, "file %s", fn);
  1189. X    if (!(fp = popen(cmdstr, "r")))
  1190. X        return(FALSE);
  1191. X    (void) strrd(ftypestr, 80, fp); /* get a line. */
  1192. X    (void) pclose(fp);    /* and done. */
  1193. X#ifdef DEBUG
  1194. X    (void) printf("%s\n", cmdstr);
  1195. X#endif
  1196. X#ifdef V_RCS
  1197. X    if (strstr(ftypestr, "text"))
  1198. X#else
  1199. X    if (strstr(ftypestr, "sccs"))
  1200. X#endif
  1201. X        return(TRUE);
  1202. X    return(FALSE);
  1203. X}
  1204. X
  1205. X/*--------------------------------------------------- strrd() ----------------
  1206. X/ read from given file pointer until a line separator or end-of-file or
  1207. X/ (len) characters excluding the terminator.
  1208. X/---------------------------------------------------------------------------*/
  1209. Xint
  1210. Xstrrd(buf, len, fle)
  1211. Xchar *buf;
  1212. Xint len;
  1213. XFILE *fle;
  1214. X{
  1215. X    int c0, i0 = 0;
  1216. X
  1217. X    while (((c0 = getc(fle)) != EOF) && c0 && c0 != '\n')
  1218. X        if(i0 < len)  /* if room in buffer..*/
  1219. X            buf[i0++] = (char) c0;  /* save it. */
  1220. X    buf[i0] = 0;
  1221. X    if (i0 == 0 && c0 == EOF)
  1222. X        return(-1);
  1223. X    return(i0);
  1224. X}
  1225. X
  1226. X/*--------------------------------------------------- getdir() --------------    
  1227. X/ get and readin variables for later.
  1228. X/---------------------------------------------------------------------------*/
  1229. Xvoid
  1230. Xgetdir()
  1231. X{
  1232. X    register char *cp;
  1233. X
  1234. X    if(cp = getenv("HOME"))        /* get user's home dir. */
  1235. X    {
  1236. X        (void) strcpy(homedir = memalloc(strlen(cp) + 2), cp);
  1237. X        if (!(s_homedir = fix_envstr(homedir)))
  1238. X        {
  1239. X            free(homedir);
  1240. X            homedir = (char *) 0;
  1241. X        }
  1242. X    }
  1243. X    else                /* this should NEVER happen */
  1244. X    {
  1245. X        (void) fprintf(stderr, "No home directory???\n");
  1246. X        exit(-1);
  1247. X    }
  1248. X
  1249. X#ifdef V_RCS
  1250. X    if(cp = getenv("RCSDIR"))    /* RCS directory */
  1251. X#else
  1252. X    if(cp = getenv("SCCSDIR"))    /* SCCS directory */
  1253. X#endif
  1254. X        (void) strcpy(rcsdir = memalloc(strlen(cp) + 2), cp);
  1255. X    else                /* RCS is $HOME/RCS */
  1256. X        (void) sprintf(rcsdir = memalloc(s_homedir + 6),
  1257. X#ifdef V_RCS
  1258. X                "%s/RCS", homedir);
  1259. X#else
  1260. X                "%s/SCCS", homedir);
  1261. X#endif
  1262. X    if (!(s_rcsdir = fix_envstr(rcsdir)))
  1263. X    {
  1264. X        free(rcsdir);
  1265. X        rcsdir = (char *) 0;
  1266. X    }
  1267. X
  1268. X#ifdef V_RCS
  1269. X    if(cp = getenv("RCSWORK"))    /* user's working directory */
  1270. X#else
  1271. X    if(cp = getenv("SCCSWORK"))    /* user's working directory */
  1272. X#endif
  1273. X    {
  1274. X        (void) strcpy(rcswrk = memalloc(strlen(cp) + 2), cp);
  1275. X        if (!(s_rcswrk = fix_envstr(rcswrk)))
  1276. X        {
  1277. X            free(rcswrk);
  1278. X            rcswrk = (char *) 0;
  1279. X        }
  1280. X    }
  1281. X
  1282. X#ifdef V_RCS
  1283. X    if (cp = getenv("RCSSRC"))    /* master source directory */
  1284. X#else
  1285. X    if (cp = getenv("SCCSSRC"))    /* master source directory */
  1286. X#endif
  1287. X    {
  1288. X        (void) strcpy(srcdir = memalloc(strlen(cp) + 2), cp);
  1289. X        if (!(s_srcdir = fix_envstr(srcdir)))
  1290. X        {
  1291. X            free(srcdir);
  1292. X            srcdir = (char *) 0;
  1293. X        }
  1294. X    }
  1295. X
  1296. X#ifdef V_RCS
  1297. X    if (cp = getenv("RCSHEAD"))    /* RCS header file directory */
  1298. X#else
  1299. X    if (cp = getenv("SCCSHEAD"))    /* SCCS header file directory */
  1300. X#endif
  1301. X    {
  1302. X        (void) strcpy(headdir = memalloc(strlen(cp) + 2), cp);
  1303. X        if (!(s_headdir = fix_envstr(headdir)))
  1304. X        {
  1305. X            free(headdir);
  1306. X            headdir = homedir;
  1307. X        }
  1308. X    }
  1309. X    else
  1310. X        headdir = homedir;
  1311. X
  1312. X#ifdef V_RCS
  1313. X    if (cp = getenv("RCSOWN"))    /* the owner of RCS files */
  1314. X#else
  1315. X    if (cp = getenv("SCCSOWN"))    /* the owner of SCCS files */
  1316. X#endif
  1317. X    {
  1318. X        s_rcsown = strlen(cp);
  1319. X        (void) strcpy(rcsown = memalloc(s_rcsown + 1), cp);
  1320. X    }
  1321. X
  1322. X    if(cp = getenv("PATH"))        /* current path, ie. $PATH */
  1323. X    {
  1324. X        s_path = strlen(cp);
  1325. X        (void) strcpy(path = memalloc(s_path + 1), cp);
  1326. X    }
  1327. X
  1328. X    if((currentdir = getcwd((char *)NULL, 200)) == NULL)
  1329. X    {
  1330. X        (void) fprintf(stderr, "Cannot get working dir.\n");
  1331. X        exit(-1);
  1332. X    }
  1333. X    s_currentdir = strlen(currentdir);
  1334. X}
  1335. X
  1336. X/*--------------------------------------------------- fix_envstr() ----------
  1337. X/ fix environment variable to avoid problems later.
  1338. X/ 1. strip leading/trailing white spaces
  1339. X/ 2. strip duplicate slashes
  1340. X/ 3. add trailing slash
  1341. X/ 4. return string length
  1342. X/---------------------------------------------------------------------------*/
  1343. Xint
  1344. Xfix_envstr(cs)
  1345. Xchar *cs;
  1346. X{
  1347. X    register char *cp, *dp;
  1348. X    register int was_slash = FALSE;
  1349. X
  1350. X    cp = dp = cs;
  1351. X    while (isspace(*cp))        /* remove leading white spaces */
  1352. X        cp++;
  1353. X
  1354. X    if (!*cp)            /* string was a full of blanks */
  1355. X        return(0);
  1356. X
  1357. X    while (*cp)
  1358. X    {
  1359. X        if (*cp == '/')
  1360. X        {
  1361. X            if (was_slash)
  1362. X            {
  1363. X                cp++;    /* strip duplicate slashes */
  1364. X                continue;
  1365. X            }
  1366. X            else
  1367. X                was_slash = TRUE;
  1368. X        }
  1369. X        else
  1370. X            was_slash = FALSE;
  1371. X        *dp++ = *cp++;
  1372. X    }
  1373. X
  1374. X    do                /* remove trailing while spaces */
  1375. X    {
  1376. X        dp--;
  1377. X    } while (isspace(*dp));
  1378. X
  1379. X    if (*dp != '/')            /* add trailing slash */
  1380. X        *++dp = '/';
  1381. X    *++dp = '\0';            /* null terminate */
  1382. X    return(strlen(cs));
  1383. X}
  1384. X
  1385. X/*--------------------------------------------------- getrcsdir() -----------
  1386. X/ get $RCSDIR + tail directory
  1387. X/---------------------------------------------------------------------------*/
  1388. Xvoid
  1389. Xgetrcsdir(tdir, sdir)
  1390. Xchar *tdir, *sdir;
  1391. X{
  1392. X    register char *cp = sdir;
  1393. X
  1394. X    if(rcswrk && !strncmp(rcswrk, cp, s_rcswrk))
  1395. X        cp += s_rcswrk;
  1396. X    if(homedir && !strncmp(homedir, cp, s_homedir))
  1397. X        cp += s_homedir;
  1398. X    /*
  1399. X     * build the final directory name
  1400. X     */
  1401. X    (void) sprintf(tdir, "%s%s", rcsdir, cp);
  1402. X}
  1403. X
  1404. X/*--------------------------------------------------- getworkdir() ----------
  1405. X/ get $RCSWORK or $HOME + tail
  1406. X/---------------------------------------------------------------------------*/
  1407. Xvoid
  1408. Xgetworkdir(tdir, sdir)
  1409. Xchar *tdir, *sdir;
  1410. X{
  1411. X    register char *cp = sdir;
  1412. X
  1413. X    if (rcsdir && !strncmp(rcsdir, cp, s_rcsdir))
  1414. X        cp += s_rcsdir;
  1415. X    (void) sprintf(tdir, "%s%s", rcswrk ? rcswrk : homedir, cp);
  1416. X}
  1417. X
  1418. X/*--------------------------------------------------- getsrcdir() -----------
  1419. X/ get $RCSSRC + tail
  1420. X/---------------------------------------------------------------------------*/
  1421. Xvoid
  1422. Xgetsrcdir(tdir, sdir)
  1423. Xchar *tdir, *sdir;
  1424. X{
  1425. X    register char *cp = sdir;
  1426. X
  1427. X    if (rcsdir && !strncmp(rcsdir, cp, s_rcsdir))
  1428. X        cp += s_rcsdir;
  1429. X    if(homedir && !strncmp(homedir, cp, s_homedir))
  1430. X        cp += s_homedir;
  1431. X    (void) sprintf(tdir, "%s%s", srcdir ? srcdir : homedir, cp);
  1432. X}
  1433. X
  1434. X/*--------------------------------------------------- getfinput() ------------
  1435. X/ get a title file.
  1436. X/---------------------------------------------------------------------------*/
  1437. Xint
  1438. Xgetfinput(name, type)
  1439. Xchar *name;        /* buffer to put file name into. */
  1440. Xint type;        /* what data we want. */
  1441. X{
  1442. X    int stat_loc;
  1443. X
  1444. X    (void) strcpy(name, tmpnam(editfile));
  1445. X    if (fork())
  1446. X    {    /* parent just waits for the child to finish */
  1447. X        (void) wait(&stat_loc);
  1448. X    }
  1449. X    else
  1450. X    {    /* child does his/her stuff */
  1451. X        (void) signal(SIGINT, SIG_DFL);
  1452. X        (void) signal(SIGQUIT, SIG_DFL);
  1453. X        exit(child_getfinput(name, type) == TRUE ? 0 : -1);
  1454. X    }
  1455. X    return((stat_loc >> 8) & 0xff);
  1456. X}
  1457. X
  1458. X/*--------------------------------------------------- child_getfinput() -----
  1459. X/ actual get title file.
  1460. X/---------------------------------------------------------------------------*/
  1461. Xstatic int
  1462. Xchild_getfinput(name, type)
  1463. Xchar *name;        /* buffer to put file name into. */
  1464. Xint type;        /* what data we want. */
  1465. X{
  1466. X    static char *input_type[2] = { "log", "title" };
  1467. X    FILE *fp, *xfp;
  1468. X    register char *st;
  1469. X    int c, done = FALSE;
  1470. X    char buf[82];            /* just larger than input buffer. */
  1471. X
  1472. X    (void) setuid(real_user_id);        /* user's real user id */
  1473. X    if((fp = fopen(name, "w")) == NULL) /* failed open. */
  1474. X    {
  1475. X        (void) unlink(name);        /* remove it. */
  1476. X        name[0] = '\0';
  1477. X        (void) printf("Unable to create tmp file.\n");
  1478. X        return(FALSE);
  1479. X    }
  1480. X    (void) printf("Enter %s message, <ret>.<ret> or Control-D to end:\n",
  1481. X        input_type[type]);
  1482. X    while (!done)
  1483. X    {
  1484. X        (void) printf(">>");
  1485. X        if(strrd(buf, 80, stdin) == -1) /* read in one line. */
  1486. X        {
  1487. X            /* ok, read somewhere that this is possible.  By doing this,
  1488. X               we should be able to continue after a control-D.
  1489. X            */
  1490. X            clearerr(stdin);
  1491. X            break;
  1492. X        }
  1493. X        if(!strcmp(".", buf))    /* end of message */
  1494. X            break;
  1495. X        if (buf[0] == '~')    /* special command */
  1496. X        {
  1497. X            switch (buf[1])    /* command character */
  1498. X            {
  1499. X            case '?':    /* print usage, help message */
  1500. X                (void) printf("\n");
  1501. X                (void) printf("~.    End of input\n");
  1502. X                (void) printf("~!    Invoke shell\n");
  1503. X                (void) printf("~e    Edit message using an editor\n");
  1504. X                (void) printf("~p    Print message buffer\n");
  1505. X                (void) printf("~r    Read in a file\n");
  1506. X                (void) printf("~w    Write message to a file\n");
  1507. X                (void) printf("~?    Print this message\n");
  1508. X                (void) printf("\n");
  1509. X                break;
  1510. X            case '!':    /* shell */
  1511. X                st = getenv("SHELL");
  1512. X                (void) system(st ? st : "/bin/sh");
  1513. X                (void) printf("[Press RETURN to continue]");
  1514. X                (void) strrd(buf, 20, stdin);
  1515. X                break;
  1516. X            case 'p':    /* print message buffer content */
  1517. X                (void) fclose(fp);
  1518. X                fp = fopen(name, "r");
  1519. X                while ((c = fgetc(fp)) != EOF)
  1520. X                    (void) fputc(c, stdout);
  1521. X                (void) fclose(fp);
  1522. X                fp = fopen(name, "a");
  1523. X                (void) printf("Continue entering %s message.\n",
  1524. X                    input_type[type]);
  1525. X                break;
  1526. X            case 'e':    /* editor */
  1527. X            case 'v':    /* visual */
  1528. X                (void) fclose(fp);
  1529. X                st = getenv(buf[1] == 'e' ?"EDITOR":"VISUAL");
  1530. X                (void) sprintf(buf, "%s %s",
  1531. X                    st ? st : "/usr/bin/vi", name);
  1532. X                (void) system(buf);
  1533. X                fp = fopen(name, "a");
  1534. X                (void) printf("Continue entering %s message.\n",
  1535. X                    input_type[type]);
  1536. X                break;
  1537. X            case 'r':    /* read in a file */
  1538. X                st = &buf[2];
  1539. X                while (isspace(*st))
  1540. X                    st++;
  1541. X                if (!*st)
  1542. X                {
  1543. X                    (void) printf("File name missing!\n");
  1544. X                    break;
  1545. X                }
  1546. X                if (xfp = fopen(st, "r"))
  1547. X                {
  1548. X                    while ((c = fgetc(xfp)) != EOF)
  1549. X                        (void) fputc(c, fp);
  1550. X                    (void) fclose(xfp);
  1551. X                }
  1552. X                else
  1553. X                    (void) printf("Unable to open %s.\n",
  1554. X                            st);
  1555. X                break;
  1556. X            case 'w':    /* write message to a file */
  1557. X                st = &buf[2];
  1558. X                while (isspace(*st))
  1559. X                    st++;
  1560. X                if (!*st)
  1561. X                    (void) printf("File name missing!\n");
  1562. X                else
  1563. X                {
  1564. X                    if (xfp = fopen(st, "a"))
  1565. X                    {
  1566. X                        (void) fclose(fp);
  1567. X                        fp = fopen(name, "r");
  1568. X                        while ((c = fgetc(fp)) != EOF)
  1569. X                            (void) fputc(c, xfp);
  1570. X                        (void) fclose(xfp);
  1571. X                        (void) fclose(fp);
  1572. X                        fp = fopen(name, "a");
  1573. X                    }
  1574. X                    else
  1575. X                        (void) printf("Unable to open %s.\n",
  1576. X                            st);
  1577. X                }
  1578. X                break;
  1579. X            case '.':    /* end of message */
  1580. X                done = TRUE;
  1581. X                break;
  1582. X            default:    /* user doesn't know */
  1583. X                (void) printf("Unrecognized command %c -- ignored\n",
  1584. X                    buf[1] & 0x7f);
  1585. X                break;
  1586. X            }
  1587. X            continue;
  1588. X        }
  1589. X        (void) fprintf(fp, "%s\n", buf);
  1590. X    }
  1591. X    (void) fclose(fp);
  1592. X    (void) printf("\n");
  1593. X    return(TRUE);
  1594. X}
  1595. X
  1596. X/*--------------------------------------------------- justname() ------------
  1597. X/ extract just filename from a full path
  1598. X/---------------------------------------------------------------------------*/
  1599. Xchar *
  1600. Xjustname(fpath)
  1601. Xchar *fpath;
  1602. X{
  1603. X    register char *cp;
  1604. X
  1605. X    if (cp = strrchr(fpath, '/'))
  1606. X        return(++cp);
  1607. X
  1608. X    return(fpath);
  1609. X}
  1610. X
  1611. X/*--------------------------------------------------- memalloc() ------------
  1612. X/ allocate specified amount of memory.  If not successful, exit.
  1613. X/---------------------------------------------------------------------------*/
  1614. Xchar *
  1615. Xmemalloc(size)
  1616. Xregister int size;
  1617. X{
  1618. X    register char *cp;
  1619. X
  1620. X    if (!(cp = malloc((unsigned)size)))
  1621. X    {
  1622. X        perror(prognam);
  1623. X        exit(99);
  1624. X    }
  1625. X    return(cp);
  1626. X}
  1627. X
  1628. X/*--------------------------------------------------- get_final_id() --------
  1629. X/ Get the RCSOWN user id to create files with.  If none found, use user id.
  1630. X/---------------------------------------------------------------------------*/
  1631. Xvoid
  1632. Xget_final_id()
  1633. X{
  1634. X    FILE *fp;
  1635. X
  1636. X    if(!rcsown)            /* if there isn't one of these. */
  1637. X#ifdef V_RCS
  1638. X        rcsown = "rcsfiles";    /* default name. */
  1639. X#else
  1640. X        rcsown = "sccsfiles";    /* default name. */
  1641. X#endif
  1642. X
  1643. X    if ((fp = fopen (pwdfile, "r")) == NULL)
  1644. X    {
  1645. X#ifdef DEBUG
  1646. X        (void) fprintf(stderr, "setpwent: %s non-existant or unreadable.\n",
  1647. X                pwdfile);
  1648. X#endif
  1649. X        user_id = real_user_id;        /* make sure it's owners id now. */
  1650. X        return;        /* couldn't do it. */
  1651. X    }
  1652. X    while (nextent(fp))        /* while entries in file.. */
  1653. X        if (!strcmp(pwdname, rcsown))   /* If name matches. */
  1654. X            break;
  1655. X    (void) fclose(fp);    /* close the file. */
  1656. X    return;            /* found it or not, return. */
  1657. X}
  1658. X
  1659. X/*--------------------------------------------------- nextent() -------------
  1660. X/ get one entry from a password file.  Return TRUE if there is one found,
  1661. X/ FALSE otherwise.
  1662. X/---------------------------------------------------------------------------*/
  1663. Xint
  1664. Xnextent(fle)
  1665. XFILE *fle;     /* file pointer. */
  1666. X{
  1667. X    register char *cp, *pwp;
  1668. X    char savbuf[200];    /* usually large enough for a password entry. */
  1669. X
  1670. X    while (strrd(savbuf, (int) (sizeof (savbuf)), fle) != -1)
  1671. X    {
  1672. X        pwp = pwdname;
  1673. X        cp = savbuf;        /* get user name */
  1674. X        while (*cp && *cp != ':')
  1675. X            *pwp++ = *cp++;
  1676. X        *pwp = '\0';        /* terminate name. */
  1677. X        for (cp++; *cp && *cp != ':'; cp++)
  1678. X            ;        /* skip over password. */
  1679. X        user_id = atoi(++cp);    /* ok, save this users id number. */
  1680. X        return (TRUE);
  1681. X    }
  1682. X    user_id = real_user_id;        /* make sure it's owners id now. */
  1683. X    return (FALSE);
  1684. X}
  1685. X
  1686. X/*----------------------------- End of cio.c -------------------------------*/
  1687. END_OF_FILE
  1688.   if test 46876 -ne `wc -c <'cio.c'`; then
  1689.     echo shar: \"'cio.c'\" unpacked with wrong size!
  1690.   fi
  1691.   # end of 'cio.c'
  1692. fi
  1693. if test -f 'cio.man' -a "${1}" != "-c" ; then 
  1694.   echo shar: Will not clobber existing file \"'cio.man'\"
  1695. else
  1696.   echo shar: Extracting \"'cio.man'\" \(10965 characters\)
  1697.   sed "s/^X//" >'cio.man' <<'END_OF_FILE'
  1698. X.TH CIO 1
  1699. X.SH NAME
  1700. Xcio - a pair of RCS user interface programs
  1701. X.SH SYNOPSIS
  1702. X.P 1
  1703. Xcii [cii options] [ci options] [filename ...] [dirname ... ]
  1704. X.P 1
  1705. Xcoo [coo options] [co options] [filename ...] [dirname ... ]
  1706. X.SH DESCRIPTION
  1707. X.I Cii
  1708. Xand
  1709. X.I coo
  1710. Xare two programs that will provide an interface to the RCS programs
  1711. X.I ci
  1712. Xand
  1713. X.I co.
  1714. XThey add:
  1715. X.P 1
  1716. XRecursively check in/out directory structures
  1717. X.P 1
  1718. XStore RCS files in a separate directory structure
  1719. X.P 1
  1720. XDefault stores only ASCII files, enabling program to
  1721. Xbe run on directories with both source and binary
  1722. X.sp
  1723. X.PP
  1724. XCommand line options include:
  1725. X.TP 5
  1726. X.B -A
  1727. XAll files.  Forces a "cp" of files that are non-ASCII.
  1728. X.TP 5
  1729. X.B -H
  1730. XInsert RCS header.  If a valid RCS header not found, a template header
  1731. Xwill be inserted at the beginning of the file being checked in.
  1732. X.TP 5
  1733. X.B -N
  1734. XNo operation.  Causes
  1735. X.I cii
  1736. Xor
  1737. X.I coo
  1738. Xto display the action that would have resulted. Nothing is executed.
  1739. X.TP 5
  1740. X.B -R
  1741. XRecursively walk down directories to check in/out
  1742. X.TP 5
  1743. X.B -T
  1744. XForce title request at start of program (Default only gets title
  1745. Xwhen required.)
  1746. X.TP 5
  1747. X.B -U
  1748. XUpdate source directory.  During a check-in, a copy is made to the
  1749. Xdirectory structure specified by $RCSSRC.  If $RCSSRC is not defined,
  1750. X$HOME will be used instead.
  1751. X.TP 5
  1752. X.B -V
  1753. XVerbose.  Be real talkative about the work being done.
  1754. X.TP 5
  1755. X.B [all ci/co options]
  1756. XPasses all other options  on to
  1757. X.I ci
  1758. Xor
  1759. X.I co.
  1760. X.TP 5
  1761. X.B filename
  1762. XOptional file names.  If not provided, the
  1763. X.I cii
  1764. Xprogram will take all files in the current directory, or
  1765. X.I coo
  1766. Xwill take all files saved in the RCS directory.
  1767. X(Binary files only if -A was also specified.)
  1768. X.TP 5
  1769. X.B dirname
  1770. XBoth programs will take a directory name as an argument, however: the
  1771. X-R option must also be specified for it to work.
  1772. X.sp 4
  1773. X.SH ENVIRONMENT
  1774. X.TP 5
  1775. X.B RCSDIR
  1776. XIf defined, names a path that will be used to store the RCS files. Default
  1777. Xis $HOME/RCS.  Final path is computed by removing $HOME and/or $RCSWORK
  1778. Xfrom current path.
  1779. X.sp
  1780. X.TP 5
  1781. X.B RCSWORK
  1782. XIf defined, an alternate prefix for current working directory.  Allows
  1783. Xhaving multiple directory structures with different prefix's.
  1784. X.sp
  1785. X.TP 5
  1786. X.B HOME
  1787. XMust be defined as the users home directory.
  1788. X.sp
  1789. X.TP 5
  1790. X.B RCSSRC
  1791. XIf defined and -U flag is specified, during a
  1792. X.I cii
  1793. Xprocedure, a copy of
  1794. Xwhat's being checked in will be made in the directory structure starting
  1795. Xat $RCSSRC.  If not specified,
  1796. X.I cii
  1797. Xuses $HOME instead.
  1798. X.sp
  1799. X.TP 5
  1800. X.B RCSHEAD
  1801. XIf -H flag is specified,
  1802. X.I cii
  1803. Xfinds template header from $RCSHEAD directory.
  1804. X.sp
  1805. X.SH SAMPLES
  1806. X.P 1
  1807. XA user with a home directory of /usr/bog, has a directory structure called
  1808. Xsample/arix.  He want's to store all of the files in /usr/bog/sample/arix
  1809. Xinto the RCS system.  If neither RCSDIR or RCSWORK is defined, the RCS
  1810. Xpath defaults to his home directory, followed by RCS, followed by the
  1811. Xcurrent path.  So:
  1812. X
  1813. X.DS
  1814. X        Current directory   :  /usr/bog/sample/arix
  1815. X        Home directory      :  /usr/bog
  1816. X        RCSDIR              :
  1817. X        RCSWORK             :
  1818. X        RCSSRC              :
  1819. X
  1820. X        Final RCS storage   :  /usr/bog/RCS/sample/arix
  1821. X        Final source storage:  /usr/bog/sample/arix
  1822. X.DE
  1823. X.P 1
  1824. XThe net effect of this command is to create the RCS directory, then to
  1825. Xduplicate the directory structure in a defined place.  In this case, since
  1826. Xthe RCSDIR was not defined, it defaulted to the users home directory.
  1827. X.P 1
  1828. XIt is not necessary to build the directories in RCS, the program will
  1829. Xbuild all necessary directories (Including the RCS dir, if needed).
  1830. X.P 1
  1831. XAnother example:  All RCS files should reside in /usr/RCS, so the 
  1832. Xabove example turns into:
  1833. X
  1834. X.DS
  1835. X        Current directory   :  /usr/bog/sample/arix
  1836. X        Home directory      :  /usr/bog
  1837. X        RCSDIR              :  /usr/RCS
  1838. X        RCSWORK             :
  1839. X        RCSSRC              :
  1840. X
  1841. X        Final RCS storage   :  /usr/RCS/sample/arix
  1842. X        Final source storage:  /usr/bog/sample/arix
  1843. X.DE
  1844. X.P 1
  1845. XHere is an example showing use of the RCSWORK variable.  Some systems
  1846. Xmay have more than one person working on file.  In this case, the path
  1847. Xnames will have to be similar, but only to a point.  Example, /usr/tog
  1848. Xis also working in a directory, called sample/arix.  His system would look
  1849. Xlike:
  1850. X
  1851. X.DS
  1852. X        Current directory   :  /usr/tog/sample/arix
  1853. X        Home directory      :  /usr/tog
  1854. X        RCSDIR              :  /usr/RCS
  1855. X        RCSWORK             :
  1856. X        RCSSRC              :
  1857. X
  1858. X        Final RCS storage   :  /usr/RCS/sample/arix
  1859. X        Final source storage:  /usr/tog/sample/arix
  1860. X.DE
  1861. X.P 1
  1862. XNote that the RCS dir is the same for him.  Now, let's take a user who
  1863. Xhas decided to work somewhere other than his home directory.
  1864. X
  1865. X.DS
  1866. X        Current directory   :  /usr/src/rog/sample/arix
  1867. X        Home directory      :  /usr/rog
  1868. X        RCSDIR              :  /usr/RCS
  1869. X        RCSWORK             :  /usr/src/rog
  1870. X        RCSSRC              :
  1871. X
  1872. X        Final RCS storage   :  /usr/RCS/sample/arix
  1873. X        Final source storage:  /usr/rog/sample/arix
  1874. X.DE
  1875. X.P 1
  1876. XIf RCSSRC is specified to keep the current source (very useful to
  1877. Xwhen one wants to browse through the current source files) in a
  1878. Xdirectory other than his HOME.
  1879. X
  1880. X.DS
  1881. X        Current directory   :  /usr/src/rog/sample/arix
  1882. X        Home directory      :  /usr/rog
  1883. X        RCSDIR              :  /usr/RCS
  1884. X        RCSWORK             :  /usr/src/rog
  1885. X        RCSSRC              :  /usr/group
  1886. X
  1887. X        Final RCS storage   :  /usr/RCS/sample/arix
  1888. X        Final source storage:  /usr/group/sample/arix
  1889. X.DE
  1890. X.P 1
  1891. XThe RCSWORK variable was removed from the current path, before the
  1892. Xdirectory structure was defined.  Thus, it is possible to be working in
  1893. Xjust about anywhere on the system and still use the same directory structure
  1894. Xand RCS files.
  1895. X
  1896. X.P 1
  1897. XIf you want to recover a directory (or multiple ones) into a new working
  1898. Xdirectory, simply create whatever part of the path you need, set the 
  1899. XRCSWORK variable to be the first part of the path, and type "coo [-R]".
  1900. X.P 1
  1901. XExample:  A new user (/usr/log) has decided to examine the sample/*
  1902. Xfiles.  Here are the steps:
  1903. X
  1904. X.DS
  1905. X      Current working directory: /usr/llog
  1906. X      mkdir sample
  1907. X      chdir sample
  1908. X      RCSDIR=/usr/RCS  export RCSDIR
  1909. X      RCSWORK=/usr/log  export RCSWORK
  1910. X.DE
  1911. X
  1912. XThis is the final setup:
  1913. X
  1914. X.DS
  1915. X      Current directory:  /usr/log/sample
  1916. X      Home directory   :  /usr/llog
  1917. X      RCSDIR           :  /usr/RCS
  1918. X      RCSWORK          :  /usr/log
  1919. X
  1920. X      Final RCS storage:  /usr/RCS/sample/arix
  1921. X.DE
  1922. X
  1923. X.SH SECURITY
  1924. X.ce 1
  1925. XSecure archives
  1926. X.P 1
  1927. X.I Cii/coo
  1928. Xwill also secure archives.  Changing ownership of the cii
  1929. Xor coo program to root, and making it suid will allow private archives.
  1930. XWith this option, an additional environment variable is searched for:
  1931. X"RCSOWN".  If this is not found, the default name "rcsfiles" is used
  1932. Xin the following step.
  1933. X.P 1
  1934. XThe user name is searched for in the /etc/password.  If not found,
  1935. Xthe real users UID will be used instead.  In this fashion, the
  1936. Xci and co programs will be called with the appropriate uses abilities to
  1937. Xcreate directories, and save files.  Only the user who owns the RCS files
  1938. Xhas write ability without going through the cii or coo programs.
  1939. X.P 1
  1940. XThe only condition that allows the program to remain in the root owned
  1941. Xmode is to have the rcsfiles have a root account.  Otherwise, it moves out
  1942. Xof the root as soon as it's found the user.
  1943. X.sp
  1944. X.SH OTHER NOTES
  1945. X.P 1
  1946. X.I Cii
  1947. Xwill not unlink files not owned by the user who is checking in the files.
  1948. XThis prevents users from deleting files not owned by them, possibly
  1949. Xcausing harm. (I.E., checking in the /etc/passwd file.)
  1950. X.I coo
  1951. Xdoes not run as root, it runs as the real user.  Hence, it is not possible
  1952. Xfor a user to overwrite files or directories they normally do not have
  1953. Xwrite access to.
  1954. X.P 1
  1955. X.I Cii/coo
  1956. Xonly will allow extraction of code by the users in the same group as the
  1957. Xuser who checked the sources in.
  1958. X.sp
  1959. X.P 1
  1960. XWe highly suggest that you exmine the code in the security area if you
  1961. Xplan on running it secure.  We've tried it secure, and it seems to work.
  1962. XHowever...  We are by no means the "great U*IX hackers", so there is
  1963. Xprobably some way to run the program that we missed that can allow others
  1964. Xaccess to restricted files.  Check it out! If you *do* find something we
  1965. Xmissed, please send us mail.
  1966. X.sp
  1967. X.SH EDITOR COMMANDS
  1968. X.P 1
  1969. XThe input editor for the logfile entries and title file entries have
  1970. Xtilde (~) command options available.  These are:
  1971. X.DS
  1972. X        ?  -  Print a help menu.
  1973. X        .  -  End of input.
  1974. X        !  -  Invoke a user shell.  Note: Shell is invoked
  1975. X              as the real user-id; No arguments are passed
  1976. X              or allowed.  The ENV variable "SHELL" is
  1977. X              searched for, if not present /bin/sh will be
  1978. X              invoked.
  1979. X        e  -  Edit the message using a default editor.
  1980. X              Searches for the ENV variable VISUAL or
  1981. X              EDITOR.  If not found, /usr/bin/vi is the
  1982. X              default editor.
  1983. X        p  -  Print message buffer.  Displays the current
  1984. X              contents of the message buffer.
  1985. X        r  -  Read in a file.  Requires file name as an
  1986. X              argument.  Named file will be appended to
  1987. X              current buffer.
  1988. X        w  -  Write message to a file.  Requires file name
  1989. X              as an argument.  Appends message buffer to
  1990. X              file name given.  If file does not exist,
  1991. X              creates file.  Does not create directories.
  1992. X.DE
  1993. X.sp 2
  1994. X.SH AUTHORS
  1995. XJason P. Winters (jason@grinch.uucp) and Craig J. Kim (cjkim@aeras.uucp)
  1996. X.SH FILES
  1997. XThe following template files can be created to insert RCS header into
  1998. Xsource files during
  1999. X.I cii
  2000. Xprocess with -H option.  The location for these files is specified by
  2001. Xsetting $RCSHEAD environment variable.  If $RCSHEAD is not set, $HOME
  2002. Xwill be used instead.  File types are determined by examining the
  2003. Xcontent via file(1) program and by file extensions (e.g. .c for C
  2004. Xprograms, .mk for makefiles, .1 for nroff, and .s for assembly).
  2005. X
  2006. X.DS
  2007. X        .rcshead       - default template
  2008. X        .rcshead.c     - C program template
  2009. X        .rcshead.s     - Assembly source template
  2010. X        .rcshead.sh    - Shell script template
  2011. X        .rcshead.f     - Fortran program template
  2012. X        .rcshead.mk    - makefile template
  2013. X        .rcshead.h     - C header file template
  2014. X        .rcshead.roff  - nroff, troff, man file template
  2015. X.DE
  2016. X.sp
  2017. X.SH DIAGNOSTICS
  2018. X.P 1
  2019. XLinking cio to ciitest or cootest will cause the program to parse the
  2020. Xdirectory paths, and print out the final pathnames.  Nothing else happens.
  2021. XUsing the -N option will cause the program to execute as normal, except
  2022. Xthat no files or directories will be created.
  2023. X.sp
  2024. X.SH BUGS
  2025. XOn some systems, the Control-D as end of input to a log or title entry
  2026. Xcan cause problems with STDIN.  We've added a call to clearerr(), but
  2027. Xhave not tested if that fixes it. ( It doesn't break it, so... )
  2028. X.P 1
  2029. XAny other bugs we would like to hear about.  We might even make a new
  2030. Xrelease, if people actually use this.  :)
  2031. X.SH AUTHOR
  2032. XJason Winters, grinch!jason
  2033. END_OF_FILE
  2034.   if test 10965 -ne `wc -c <'cio.man'`; then
  2035.     echo shar: \"'cio.man'\" unpacked with wrong size!
  2036.   fi
  2037.   # end of 'cio.man'
  2038. fi
  2039. echo shar: End of archive.
  2040. exit 0
  2041. exit 0 # Just in case...
  2042.